/******************************************************************************* * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.ui.dialogs; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MenuAdapter; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.ProgressMonitorWrapper; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.progress.UIJob; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.TypeNameMatch; import org.eclipse.jdt.core.search.TypeNameMatchRequestor; import org.eclipse.jdt.core.search.TypeNameRequestor; import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.internal.corext.util.OpenTypeHistory; import org.eclipse.jdt.internal.corext.util.Strings; import org.eclipse.jdt.internal.corext.util.TypeFilter; import org.eclipse.jdt.internal.corext.util.TypeInfoFilter; import org.eclipse.jdt.internal.corext.util.TypeInfoRequestorAdapter; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.IVMInstallType; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; import org.eclipse.jdt.ui.JavaElementLabels; import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension; import org.eclipse.jdt.ui.dialogs.ITypeInfoImageProvider; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.JavaPluginImages; import org.eclipse.jdt.internal.ui.JavaUIMessages; import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider; /** * DO NOT REMOVE, used in a product. * @deprecated As of 3.5, replaced by {@link org.eclipse.ui.dialogs.FilteredItemsSelectionDialog} */ @SuppressWarnings("all") public class TypeInfoViewer { private static class SearchRequestor extends TypeNameMatchRequestor { private volatile boolean fStop; private Set fHistory; private TypeInfoFilter fFilter; private List fResult; public SearchRequestor(TypeInfoFilter filter) { super(); fResult= new ArrayList(2048); fFilter= filter; } public TypeNameMatch[] getResult() { return (TypeNameMatch[])fResult.toArray(new TypeNameMatch[fResult.size()]); } public void cancel() { fStop= true; } public void setHistory(Set history) { fHistory= history; } /* (non-Javadoc) * @see org.eclipse.jdt.core.search.TypeNameMatchRequestor#acceptTypeNameMatch(org.eclipse.jdt.core.search.TypeNameMatch) */ @Override public void acceptTypeNameMatch(TypeNameMatch match) { if (fStop) return; if (TypeFilter.isFiltered(match)) return; if (fHistory.contains(match)) return; if (fFilter.matchesFilterExtension(match)) fResult.add(match); } } protected static class TypeInfoComparator implements Comparator { private TypeInfoLabelProvider fLabelProvider; private TypeInfoFilter fFilter; public TypeInfoComparator(TypeInfoLabelProvider labelProvider, TypeInfoFilter filter) { fLabelProvider= labelProvider; fFilter= filter; } public int compare(Object left, Object right) { TypeNameMatch leftInfo= (TypeNameMatch)left; TypeNameMatch rightInfo= (TypeNameMatch)right; int leftCategory= getCamelCaseCategory(leftInfo); int rightCategory= getCamelCaseCategory(rightInfo); if (leftCategory < rightCategory) return -1; if (leftCategory > rightCategory) return +1; int result= compareName(leftInfo.getSimpleTypeName(), rightInfo.getSimpleTypeName()); if (result != 0) return result; result= compareTypeContainerName(leftInfo.getTypeContainerName(), rightInfo.getTypeContainerName()); if (result != 0) return result; leftCategory= getElementTypeCategory(leftInfo); rightCategory= getElementTypeCategory(rightInfo); if (leftCategory < rightCategory) return -1; if (leftCategory > rightCategory) return +1; return compareContainerName(leftInfo, rightInfo); } private int compareName(String leftString, String rightString) { int result= leftString.compareToIgnoreCase(rightString); if (result != 0 || rightString.length() == 0) { return result; } else if (Strings.isLowerCase(leftString.charAt(0)) && !Strings.isLowerCase(rightString.charAt(0))) { return +1; } else if (Strings.isLowerCase(rightString.charAt(0)) && !Strings.isLowerCase(leftString.charAt(0))) { return -1; } else { return leftString.compareTo(rightString); } } private int compareTypeContainerName(String leftString, String rightString) { int leftLength= leftString.length(); int rightLength= rightString.length(); if (leftLength == 0 && rightLength > 0) return -1; if (leftLength == 0 && rightLength == 0) return 0; if (leftLength > 0 && rightLength == 0) return +1; return compareName(leftString, rightString); } private int compareContainerName(TypeNameMatch leftType, TypeNameMatch rightType) { return fLabelProvider.getContainerName(leftType).compareTo( fLabelProvider.getContainerName(rightType)); } private int getCamelCaseCategory(TypeNameMatch type) { if (fFilter == null) return 0; if (!fFilter.isCamelCasePattern()) return 0; return fFilter.matchesRawNamePattern(type) ? 0 : 1; } private int getElementTypeCategory(TypeNameMatch type) { try { if (type.getPackageFragmentRoot().getKind() == IPackageFragmentRoot.K_SOURCE) return 0; } catch (JavaModelException e) { // TODO Auto-generated catch block e.printStackTrace(); } return 1; } } protected static class TypeInfoLabelProvider { private ITypeInfoImageProvider fProviderExtension; private TypeInfoRequestorAdapter fAdapter= new TypeInfoRequestorAdapter(); private Map fLib2Name= new HashMap(); private String[] fInstallLocations; private String[] fVMNames; private boolean fFullyQualifyDuplicates; public TypeInfoLabelProvider(ITypeInfoImageProvider extension) { fProviderExtension= extension; List locations= new ArrayList(); List labels= new ArrayList(); IVMInstallType[] installs= JavaRuntime.getVMInstallTypes(); for (int i= 0; i < installs.length; i++) { processVMInstallType(installs[i], locations, labels); } fInstallLocations= (String[])locations.toArray(new String[locations.size()]); fVMNames= (String[])labels.toArray(new String[labels.size()]); } public void setFullyQualifyDuplicates(boolean value) { fFullyQualifyDuplicates= value; } private void processVMInstallType(IVMInstallType installType, List locations, List labels) { if (installType != null) { IVMInstall[] installs= installType.getVMInstalls(); boolean isMac= Platform.OS_MACOSX.equals(Platform.getOS()); final String HOME_SUFFIX= "/Home"; //$NON-NLS-1$ for (int i= 0; i < installs.length; i++) { String label= getFormattedLabel(installs[i].getName()); LibraryLocation[] libLocations= installs[i].getLibraryLocations(); if (libLocations != null) { processLibraryLocation(libLocations, label); } else { String filePath= installs[i].getInstallLocation().getAbsolutePath(); // on MacOS X install locations end in an additional "/Home" segment; remove it if (isMac && filePath.endsWith(HOME_SUFFIX)) filePath= filePath.substring(0, filePath.length()- HOME_SUFFIX.length() + 1); locations.add(filePath); labels.add(label); } } } } private void processLibraryLocation(LibraryLocation[] libLocations, String label) { for (int l= 0; l < libLocations.length; l++) { LibraryLocation location= libLocations[l]; fLib2Name.put(location.getSystemLibraryPath().toString(), label); } } private String getFormattedLabel(String name) { return Messages.format(JavaUIMessages.TypeInfoViewer_library_name_format, name); } public String getText(Object element) { return ((TypeNameMatch)element).getSimpleTypeName(); } public String getQualifiedText(TypeNameMatch type) { StringBuffer result= new StringBuffer(); result.append(type.getSimpleTypeName()); String containerName= type.getTypeContainerName(); result.append(JavaElementLabels.CONCAT_STRING); if (containerName.length() > 0) { result.append(containerName); } else { result.append(JavaUIMessages.TypeInfoViewer_default_package); } return result.toString(); } public String getFullyQualifiedText(TypeNameMatch type) { StringBuffer result= new StringBuffer(); result.append(type.getSimpleTypeName()); String containerName= type.getTypeContainerName(); if (containerName.length() > 0) { result.append(JavaElementLabels.CONCAT_STRING); result.append(containerName); } result.append(JavaElementLabels.CONCAT_STRING); result.append(getContainerName(type)); return result.toString(); } public String getText(TypeNameMatch last, TypeNameMatch current, TypeNameMatch next) { StringBuffer result= new StringBuffer(); int qualifications= 0; String currentTN= current.getSimpleTypeName(); result.append(currentTN); String currentTCN= getTypeContainerName(current); if (last != null) { String lastTN= last.getSimpleTypeName(); String lastTCN= getTypeContainerName(last); if (currentTCN.equals(lastTCN)) { if (currentTN.equals(lastTN)) { result.append(JavaElementLabels.CONCAT_STRING); result.append(currentTCN); result.append(JavaElementLabels.CONCAT_STRING); result.append(getContainerName(current)); return result.toString(); } } else if (currentTN.equals(lastTN)) { qualifications= 1; } } if (next != null) { String nextTN= next.getSimpleTypeName(); String nextTCN= getTypeContainerName(next); if (currentTCN.equals(nextTCN)) { if (currentTN.equals(nextTN)) { result.append(JavaElementLabels.CONCAT_STRING); result.append(currentTCN); result.append(JavaElementLabels.CONCAT_STRING); result.append(getContainerName(current)); return result.toString(); } } else if (currentTN.equals(nextTN)) { qualifications= 1; } } if (qualifications > 0) { result.append(JavaElementLabels.CONCAT_STRING); result.append(currentTCN); if (fFullyQualifyDuplicates) { result.append(JavaElementLabels.CONCAT_STRING); result.append(getContainerName(current)); } } return result.toString(); } public String getQualificationText(TypeNameMatch type) { StringBuffer result= new StringBuffer(); String containerName= type.getTypeContainerName(); if (containerName.length() > 0) { result.append(containerName); result.append(JavaElementLabels.CONCAT_STRING); } result.append(getContainerName(type)); return result.toString(); } private boolean isInnerType(TypeNameMatch match) { return match.getTypeQualifiedName().indexOf('.') != -1; } public ImageDescriptor getImageDescriptor(Object element) { TypeNameMatch type= (TypeNameMatch)element; if (fProviderExtension != null) { fAdapter.setMatch(type); ImageDescriptor descriptor= fProviderExtension.getImageDescriptor(fAdapter); if (descriptor != null) return descriptor; } return JavaElementImageProvider.getTypeImageDescriptor( isInnerType(type), false, type.getModifiers(), false); } private String getTypeContainerName(TypeNameMatch info) { String result= info.getTypeContainerName(); if (result.length() > 0) return result; return JavaUIMessages.TypeInfoViewer_default_package; } private String getContainerName(TypeNameMatch type) { IPackageFragmentRoot root= type.getPackageFragmentRoot(); if (root.isExternal()) { String name= root.getPath().toOSString(); for (int i= 0; i < fInstallLocations.length; i++) { if (name.startsWith(fInstallLocations[i])) { return fVMNames[i]; } } String lib= (String)fLib2Name.get(name); if (lib != null) return lib; } StringBuffer buf= new StringBuffer(); JavaElementLabels.getPackageFragmentRootLabel(root, JavaElementLabels.ROOT_QUALIFIED | JavaElementLabels.ROOT_VARIABLE, buf); return buf.toString(); } } private static class ProgressUpdateJob extends UIJob { private TypeInfoViewer fViewer; private boolean fStopped; public ProgressUpdateJob(Display display, TypeInfoViewer viewer) { super(display, JavaUIMessages.TypeInfoViewer_progressJob_label); fViewer= viewer; } public void stop() { fStopped= true; cancel(); } @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (stopped()) return new Status(IStatus.CANCEL, JavaPlugin.getPluginId(), IStatus.CANCEL, "", null); //$NON-NLS-1$ fViewer.updateProgressMessage(); if (!stopped()) schedule(300); return new Status(IStatus.OK, JavaPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$ } private boolean stopped() { return fStopped || fViewer.getTable().isDisposed(); } } private static class ProgressMonitor extends ProgressMonitorWrapper { private TypeInfoViewer fViewer; private String fName; private int fTotalWork; private double fWorked; private boolean fDone; public ProgressMonitor(IProgressMonitor monitor, TypeInfoViewer viewer) { super(monitor); fViewer= viewer; } @Override public void setTaskName(String name) { super.setTaskName(name); fName= name; } @Override public void beginTask(String name, int totalWork) { super.beginTask(name, totalWork); if (fName == null) fName= name; fTotalWork= totalWork; } @Override public void worked(int work) { super.worked(work); internalWorked(work); } @Override public void done() { fDone= true; fViewer.setProgressMessage(""); //$NON-NLS-1$ super.done(); } @Override public void internalWorked(double work) { fWorked= fWorked + work; fViewer.setProgressMessage(getMessage()); } private String getMessage() { if (fDone) { return ""; //$NON-NLS-1$ } else if (fTotalWork == 0) { return fName; } else { return Messages.format( JavaUIMessages.TypeInfoViewer_progress_label, new Object[] { fName, new Integer((int)((fWorked * 100) / fTotalWork)) }); } } } private static abstract class AbstractJob extends Job { protected TypeInfoViewer fViewer; protected AbstractJob(String name, TypeInfoViewer viewer) { super(name); fViewer= viewer; setSystem(true); } @Override protected final IStatus run(IProgressMonitor parent) { ProgressMonitor monitor= new ProgressMonitor(parent, fViewer); try { fViewer.scheduleProgressUpdateJob(); return doRun(monitor); } finally { fViewer.stopProgressUpdateJob(); } } protected abstract IStatus doRun(ProgressMonitor monitor); } private static abstract class AbstractSearchJob extends AbstractJob { private int fMode; protected int fTicket; protected TypeInfoLabelProvider fLabelProvider; protected TypeInfoFilter fFilter; protected OpenTypeHistory fHistory; protected AbstractSearchJob(int ticket, TypeInfoViewer viewer, TypeInfoFilter filter, OpenTypeHistory history, int numberOfVisibleItems, int mode) { super(JavaUIMessages.TypeInfoViewer_job_label, viewer); fMode= mode; fTicket= ticket; fViewer= viewer; fLabelProvider= fViewer.getLabelProvider(); fFilter= filter; fHistory= history; } public void stop() { cancel(); } @Override protected IStatus doRun(ProgressMonitor monitor) { try { if (VIRTUAL) { internalRunVirtual(monitor); } else { internalRun(monitor); } } catch (CoreException e) { fViewer.searchJobFailed(fTicket, e); return new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.ERROR, JavaUIMessages.TypeInfoViewer_job_error, e); } catch (InterruptedException e) { return canceled(e, true); } catch (OperationCanceledException e) { return canceled(e, false); } fViewer.searchJobDone(fTicket); return ok(); } protected abstract TypeNameMatch[] getSearchResult(Set matchIdsInHistory, ProgressMonitor monitor) throws CoreException; private void internalRun(ProgressMonitor monitor) throws CoreException, InterruptedException { if (monitor.isCanceled()) throw new OperationCanceledException(); fViewer.clear(fTicket); // local vars to speed up rendering TypeNameMatch last= null; TypeNameMatch type= null; TypeNameMatch next= null; List elements= new ArrayList(); List imageDescriptors= new ArrayList(); List labels= new ArrayList(); Set filteredMatches= new HashSet(); TypeNameMatch[] matchingTypes= fHistory.getFilteredTypeInfos(fFilter); if (matchingTypes.length > 0) { Arrays.sort(matchingTypes, new TypeInfoComparator(fLabelProvider, fFilter)); type= matchingTypes[0]; int i= 1; while(type != null) { next= (i == matchingTypes.length) ? null : matchingTypes[i]; elements.add(type); filteredMatches.add(type); imageDescriptors.add(fLabelProvider.getImageDescriptor(type)); labels.add(fLabelProvider.getText(last, type, next)); last= type; type= next; i++; } } matchingTypes= null; fViewer.fExpectedItemCount= elements.size(); fViewer.addHistory(fTicket, elements, imageDescriptors, labels); if ((fMode & INDEX) == 0) { return; } TypeNameMatch[] result= getSearchResult(filteredMatches, monitor); fViewer.fExpectedItemCount+= result.length; if (result.length == 0) { return; } if (monitor.isCanceled()) throw new OperationCanceledException(); int processed= 0; int nextIndex= 1; type= result[0]; if (!filteredMatches.isEmpty()) { fViewer.addDashLineAndUpdateLastHistoryEntry(fTicket, type); } while (true) { long startTime= System.currentTimeMillis(); elements.clear(); imageDescriptors.clear(); labels.clear(); int delta = Math.min(nextIndex == 1 ? fViewer.getNumberOfVisibleItems() : 10, result.length - processed); if (delta == 0) break; processed= processed + delta; while(delta > 0) { next= (nextIndex == result.length) ? null : result[nextIndex]; elements.add(type); labels.add(fLabelProvider.getText(last, type, next)); imageDescriptors.add(fLabelProvider.getImageDescriptor(type)); last= type; type= next; nextIndex++; delta--; } fViewer.addAll(fTicket, elements, imageDescriptors, labels); long sleep= 100 - (System.currentTimeMillis() - startTime); if (sleep > 0) Thread.sleep(sleep); if (monitor.isCanceled()) throw new OperationCanceledException(); } } private void internalRunVirtual(ProgressMonitor monitor) throws CoreException, InterruptedException { if (monitor.isCanceled()) throw new OperationCanceledException(); fViewer.clear(fTicket); TypeNameMatch[] matchingTypes= fHistory.getFilteredTypeInfos(fFilter); fViewer.setHistoryResult(fTicket, matchingTypes); if ((fMode & INDEX) == 0) return; Set filteredMatches= new HashSet(matchingTypes.length * 2); for (int i= 0; i < matchingTypes.length; i++) { filteredMatches.add(matchingTypes[i]); } TypeNameMatch[] result= getSearchResult(filteredMatches, monitor); if (monitor.isCanceled()) throw new OperationCanceledException(); fViewer.setSearchResult(fTicket, result); } private IStatus canceled(Exception e, boolean removePendingItems) { fViewer.searchJobCanceled(fTicket, removePendingItems); return new Status(IStatus.CANCEL, JavaPlugin.getPluginId(), IStatus.CANCEL, JavaUIMessages.TypeInfoViewer_job_cancel, e); } private IStatus ok() { return new Status(IStatus.OK, JavaPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$ } } private static class SearchEngineJob extends AbstractSearchJob { private IJavaSearchScope fScope; private int fElementKind; private SearchRequestor fReqestor; public SearchEngineJob(int ticket, TypeInfoViewer viewer, TypeInfoFilter filter, OpenTypeHistory history, int numberOfVisibleItems, int mode, IJavaSearchScope scope, int elementKind) { super(ticket, viewer, filter, history, numberOfVisibleItems, mode); fScope= scope; fElementKind= elementKind; fReqestor= new SearchRequestor(filter); } @Override public void stop() { fReqestor.cancel(); super.stop(); } @Override protected TypeNameMatch[] getSearchResult(Set matchIdsInHistory, ProgressMonitor monitor) throws CoreException { long start= System.currentTimeMillis(); fReqestor.setHistory(matchIdsInHistory); // consider primary working copies during searching SearchEngine engine= new SearchEngine((WorkingCopyOwner)null); String packPattern= fFilter.getPackagePattern(); monitor.setTaskName(JavaUIMessages.TypeInfoViewer_searchJob_taskName); engine.searchAllTypeNames( packPattern == null ? null : packPattern.toCharArray(), fFilter.getPackageFlags(), fFilter.getNamePattern().toCharArray(), fFilter.getSearchFlags(), fElementKind, fScope, fReqestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor); if (DEBUG) System.out.println("Time needed until search has finished: " + (System.currentTimeMillis() - start)); //$NON-NLS-1$ TypeNameMatch[] result= fReqestor.getResult(); Arrays.sort(result, new TypeInfoComparator(fLabelProvider, fFilter)); if (DEBUG) System.out.println("Time needed until sort has finished: " + (System.currentTimeMillis() - start)); //$NON-NLS-1$ fViewer.rememberResult(fTicket, result); return result; } } private static class CachedResultJob extends AbstractSearchJob { private TypeNameMatch[] fLastResult; public CachedResultJob(int ticket, TypeNameMatch[] lastResult, TypeInfoViewer viewer, TypeInfoFilter filter, OpenTypeHistory history, int numberOfVisibleItems, int mode) { super(ticket, viewer, filter, history, numberOfVisibleItems, mode); fLastResult= lastResult; } @Override protected TypeNameMatch[] getSearchResult(Set filteredHistory, ProgressMonitor monitor) throws CoreException { List result= new ArrayList(2048); for (int i= 0; i < fLastResult.length; i++) { TypeNameMatch type= fLastResult[i]; if (filteredHistory.contains(type)) continue; if (fFilter.matchesCachedResult(type)) result.add(type); } // we have to sort if the filter is a camel case filter. TypeNameMatch[] types= (TypeNameMatch[])result.toArray(new TypeNameMatch[result.size()]); if (fFilter.isCamelCasePattern()) { Arrays.sort(types, new TypeInfoComparator(fLabelProvider, fFilter)); } return types; } } private static class SyncJob extends AbstractJob { public SyncJob(TypeInfoViewer viewer) { super(JavaUIMessages.TypeInfoViewer_syncJob_label, viewer); } public void stop() { cancel(); } @Override protected IStatus doRun(ProgressMonitor monitor) { try { monitor.setTaskName(JavaUIMessages.TypeInfoViewer_syncJob_taskName); new SearchEngine().searchAllTypeNames( null, 0, // make sure we search a concrete name. This is faster according to Kent "_______________".toCharArray(), //$NON-NLS-1$ SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, IJavaSearchConstants.ENUM, SearchEngine.createWorkspaceScope(), new TypeNameRequestor() {}, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor); } catch (JavaModelException e) { JavaPlugin.log(e); return new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.ERROR, JavaUIMessages.TypeInfoViewer_job_error, e); } catch (OperationCanceledException e) { return new Status(IStatus.CANCEL, JavaPlugin.getPluginId(), IStatus.CANCEL, JavaUIMessages.TypeInfoViewer_job_cancel, e); } finally { fViewer.syncJobDone(); } return new Status(IStatus.OK, JavaPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$ } } private static class DashLine { private int fSeparatorWidth; private String fMessage; private int fMessageLength; public String getText(int width) { StringBuffer dashes= new StringBuffer(); int chars= (((width - fMessageLength) / fSeparatorWidth) / 2) -2; for (int i= 0; i < chars; i++) { dashes.append(SEPARATOR); } StringBuffer result= new StringBuffer(); result.append(dashes); result.append(fMessage); result.append(dashes); return result.toString(); } public void initialize(GC gc) { fSeparatorWidth= gc.getAdvanceWidth(SEPARATOR); fMessage= " " + JavaUIMessages.TypeInfoViewer_separator_message + " "; //$NON-NLS-1$ //$NON-NLS-2$ fMessageLength= gc.textExtent(fMessage).x; } } private static class ImageManager { private Map fImages= new HashMap(20); public Image get(ImageDescriptor descriptor) { if (descriptor == null) descriptor= ImageDescriptor.getMissingImageDescriptor(); Image result= (Image)fImages.get(descriptor); if (result != null) return result; result= descriptor.createImage(); if (result != null) fImages.put(descriptor, result); return result; } public void dispose() { for (Iterator iter= fImages.values().iterator(); iter.hasNext(); ) { Image image= (Image)iter.next(); image.dispose(); } fImages.clear(); } } private Display fDisplay; private String fProgressMessage; private Label fProgressLabel; private int fProgressCounter; private ProgressUpdateJob fProgressUpdateJob; private OpenTypeHistory fHistory; /* non virtual table */ private int fNextElement; private List fItems; /* virtual table */ private TypeNameMatch[] fHistoryMatches; private TypeNameMatch[] fSearchMatches; private int fNumberOfVisibleItems; private int fExpectedItemCount; private Color fDashLineColor; private int fScrollbarWidth; private int fTableWidthDelta; private int fDashLineIndex= -1; private Image fSeparatorIcon; private DashLine fDashLine= new DashLine(); private boolean fFullyQualifySelection; /* remembers the last selection to restore unqualified labels */ private TableItem[] fLastSelection; private String[] fLastLabels; private TypeInfoLabelProvider fLabelProvider; private ImageManager fImageManager; private Table fTable; private SyncJob fSyncJob; private TypeInfoFilter fTypeInfoFilter; private ITypeInfoFilterExtension fFilterExtension; private TypeNameMatch[] fLastCompletedResult; private TypeInfoFilter fLastCompletedFilter; private int fSearchJobTicket; protected int fElementKind; protected IJavaSearchScope fSearchScope; private AbstractSearchJob fSearchJob; private static final int HISTORY= 1; private static final int INDEX= 2; private static final int FULL= HISTORY | INDEX; private static final char SEPARATOR= '-'; private static final boolean DEBUG= false; private static final boolean VIRTUAL= false; private static final TypeNameMatch[] EMTPY_TYPE_INFO_ARRAY= new TypeNameMatch[0]; // only needed when in virtual table mode private static final TypeNameMatch DASH_LINE= SearchEngine.createTypeNameMatch(null, 0); public TypeInfoViewer(Composite parent, int flags, Label progressLabel, IJavaSearchScope scope, int elementKind, String initialFilter, ITypeInfoFilterExtension filterExtension, ITypeInfoImageProvider imageExtension) { Assert.isNotNull(scope); fDisplay= parent.getDisplay(); fProgressLabel= progressLabel; fSearchScope= scope; fElementKind= elementKind; fFilterExtension= filterExtension; fFullyQualifySelection= (flags & SWT.MULTI) != 0; if (VIRTUAL) flags|= SWT.VIRTUAL; fTable= new Table(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.FLAT | flags); fTable.setFont(parent.getFont()); fLabelProvider= new TypeInfoLabelProvider(imageExtension); fItems= new ArrayList(500); fTable.setHeaderVisible(false); addPopupMenu(); fTable.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent event) { int itemHeight= fTable.getItemHeight(); Rectangle clientArea= fTable.getClientArea(); fNumberOfVisibleItems= (clientArea.height / itemHeight) + 1; } }); fTable.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.DEL) { deleteHistoryEntry(); } else if (e.keyCode == SWT.ARROW_DOWN) { int index= fTable.getSelectionIndex(); if (index == fDashLineIndex - 1) { e.doit= false; setTableSelection(index + 2); } } else if (e.keyCode == SWT.ARROW_UP) { int index= fTable.getSelectionIndex(); if (fDashLineIndex != -1 && index == fDashLineIndex + 1) { e.doit= false; setTableSelection(index - 2); } } } }); fTable.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (fLastSelection != null) { for (int i= 0; i < fLastSelection.length; i++) { TableItem item= fLastSelection[i]; // could be disposed by deleting element from // type info history if (!item.isDisposed()) item.setText(fLastLabels[i]); } } TableItem[] items= fTable.getSelection(); fLastSelection= new TableItem[items.length]; fLastLabels= new String[items.length]; for (int i= 0; i < items.length; i++) { TableItem item= items[i]; fLastSelection[i]= item; fLastLabels[i]= item.getText(); Object data= item.getData(); if (data instanceof TypeNameMatch) { String qualifiedText= getQualifiedText((TypeNameMatch)data); if (qualifiedText.length() > fLastLabels[i].length()) item.setText(qualifiedText); } } } }); fTable.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { stop(true, true); fDashLineColor.dispose(); fSeparatorIcon.dispose(); fImageManager.dispose(); if (fProgressUpdateJob != null) { fProgressUpdateJob.stop(); fProgressUpdateJob= null; } } }); if (VIRTUAL) { fHistoryMatches= EMTPY_TYPE_INFO_ARRAY; fSearchMatches= EMTPY_TYPE_INFO_ARRAY; fTable.addListener(SWT.SetData, new Listener() { public void handleEvent(Event event) { TableItem item= (TableItem)event.item; setData(item); } }); } fDashLineColor= computeDashLineColor(); fScrollbarWidth= computeScrollBarWidth(); fTableWidthDelta= fTable.computeTrim(0, 0, 0, 0).width - fScrollbarWidth; fSeparatorIcon= JavaPluginImages.DESC_OBJS_TYPE_SEPARATOR.createImage(fTable.getDisplay()); // Use a new image manager since an extension can provide its own // image descriptors. To avoid thread problems with SWT the registry // must be created in the UI thread. fImageManager= new ImageManager(); fHistory= OpenTypeHistory.getInstance(); if (initialFilter != null && initialFilter.length() > 0) fTypeInfoFilter= createTypeInfoFilter(initialFilter); GC gc= null; try { gc= new GC(fTable); gc.setFont(fTable.getFont()); fDashLine.initialize(gc); } finally { gc.dispose(); } // If we do have a type info filter then we are // scheduling a search job in startup. So no // need to sync the search indices. if (fTypeInfoFilter == null) { scheduleSyncJob(); } } /* package */ void startup() { if (fTypeInfoFilter == null) { reset(); } else { scheduleSearchJob(FULL); } } public Table getTable() { return fTable; } /* package */ TypeInfoLabelProvider getLabelProvider() { return fLabelProvider; } private int getNumberOfVisibleItems() { return fNumberOfVisibleItems; } public void setFocus() { fTable.setFocus(); } public void setQualificationStyle(boolean value) { if (fFullyQualifySelection == value) return; fFullyQualifySelection= value; if (fLastSelection != null) { for (int i= 0; i < fLastSelection.length; i++) { TableItem item= fLastSelection[i]; Object data= item.getData(); if (data instanceof TypeNameMatch) { item.setText(getQualifiedText((TypeNameMatch)data)); } } } } public TypeNameMatch[] getSelection() { TableItem[] items= fTable.getSelection(); List result= new ArrayList(items.length); for (int i= 0; i < items.length; i++) { Object data= items[i].getData(); if (data instanceof TypeNameMatch) { result.add(data); } } return (TypeNameMatch[])result.toArray(new TypeNameMatch[result.size()]); } public void stop() { stop(true, false); } public void stop(boolean stopSyncJob, boolean dispose) { if (fSyncJob != null && stopSyncJob) { fSyncJob.stop(); fSyncJob= null; } if (fSearchJob != null) { fSearchJob.stop(); fSearchJob= null; } } public void forceSearch() { stop(false, false); if (fTypeInfoFilter == null) { reset(); } else { // clear last results fLastCompletedFilter= null; fLastCompletedResult= null; scheduleSearchJob(isSyncJobRunning() ? HISTORY : FULL); } } public void setSearchPattern(String text) { stop(false, false); if (text.length() == 0 || "*".equals(text)) { //$NON-NLS-1$ fTypeInfoFilter= null; reset(); } else { fTypeInfoFilter= createTypeInfoFilter(text); scheduleSearchJob(isSyncJobRunning() ? HISTORY : FULL); } } public void setSearchScope(IJavaSearchScope scope, boolean refresh) { fSearchScope= scope; if (!refresh) return; stop(false, false); fLastCompletedFilter= null; fLastCompletedResult= null; if (fTypeInfoFilter == null) { reset(); } else { scheduleSearchJob(isSyncJobRunning() ? HISTORY : FULL); } } public void setFullyQualifyDuplicates(boolean value, boolean refresh) { fLabelProvider.setFullyQualifyDuplicates(value); if (!refresh) return; stop(false, false); if (fTypeInfoFilter == null) { reset(); } else { scheduleSearchJob(isSyncJobRunning() ? HISTORY : FULL); } } public void reset() { fLastSelection= null; fLastLabels= null; fExpectedItemCount= 0; fDashLineIndex= -1; TypeInfoFilter filter= (fTypeInfoFilter != null) ? fTypeInfoFilter : new TypeInfoFilter("*", fSearchScope, fElementKind, fFilterExtension); //$NON-NLS-1$ if (VIRTUAL) { fHistoryMatches= fHistory.getFilteredTypeInfos(filter); fExpectedItemCount= fHistoryMatches.length; fTable.setItemCount(fHistoryMatches.length); // bug under windows. if (fHistoryMatches.length == 0) { fTable.redraw(); } fTable.clear(0, fHistoryMatches.length - 1); } else { fNextElement= 0; TypeNameMatch[] historyItems= fHistory.getFilteredTypeInfos(filter); if (historyItems.length == 0) { shortenTable(); return; } fExpectedItemCount= historyItems.length; int lastIndex= historyItems.length - 1; TypeNameMatch last= null; TypeNameMatch type= historyItems[0]; for (int i= 0; i < historyItems.length; i++) { TypeNameMatch next= i == lastIndex ? null : historyItems[i + 1]; addSingleElement(type, fLabelProvider.getImageDescriptor(type), fLabelProvider.getText(last, type, next)); last= type; type= next; } shortenTable(); } } protected TypeInfoFilter createTypeInfoFilter(String text) { if ("**".equals(text)) //$NON-NLS-1$ text= "*"; //$NON-NLS-1$ return new TypeInfoFilter(text, fSearchScope, fElementKind, fFilterExtension); } private void addPopupMenu() { Menu menu= new Menu(fTable.getShell(), SWT.POP_UP); fTable.setMenu(menu); final MenuItem remove= new MenuItem(menu, SWT.NONE); remove.setText(JavaUIMessages.TypeInfoViewer_remove_from_history); menu.addMenuListener(new MenuAdapter() { @Override public void menuShown(MenuEvent e) { TableItem[] selection= fTable.getSelection(); remove.setEnabled(canEnable(selection)); } }); remove.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { deleteHistoryEntry(); } }); } private boolean canEnable(TableItem[] selection) { if (selection.length == 0) return false; for (int i= 0; i < selection.length; i++) { TableItem item= selection[i]; Object data= item.getData(); if (!(data instanceof TypeNameMatch)) return false; if (!(fHistory.contains((TypeNameMatch)data))) return false; } return true; } //---- History management ------------------------------------------------------- private void deleteHistoryEntry() { int index= fTable.getSelectionIndex(); if (index == -1) return; TableItem item= fTable.getItem(index); Object element= item.getData(); if (!(element instanceof TypeNameMatch)) return; if (fHistory.remove(element) != null) { item.dispose(); fItems.remove(index); int count= fTable.getItemCount(); if (count > 0) { item= fTable.getItem(0); if (item.getData() instanceof DashLine) { item.dispose(); fItems.remove(0); fDashLineIndex= -1; if (count > 1) { setTableSelection(0); } } else { if (index >= count) { index= count - 1; } setTableSelection(index); } } else { // send dummy selection fTable.notifyListeners(SWT.Selection, new Event()); } } } //-- Search result updating ---------------------------------------------------- private void clear(int ticket) { syncExec(ticket, new Runnable() { public void run() { fNextElement= 0; fDashLineIndex= -1; fLastSelection= null; fLastLabels= null; fExpectedItemCount= 0; } }); } private void rememberResult(int ticket, final TypeNameMatch[] result) { syncExec(ticket, new Runnable() { public void run() { if (fLastCompletedResult == null) { fLastCompletedFilter= fTypeInfoFilter; fLastCompletedResult= result; } } }); } private void addHistory(int ticket, final List elements, final List imageDescriptors, final List labels) { addAll(ticket, elements, imageDescriptors, labels); } private void addAll(int ticket, final List elements, final List imageDescriptors, final List labels) { syncExec(ticket, new Runnable() { public void run() { int size= elements.size(); for(int i= 0; i < size; i++) { addSingleElement(elements.get(i), (ImageDescriptor)imageDescriptors.get(i), (String)labels.get(i)); } } }); } private void addDashLineAndUpdateLastHistoryEntry(int ticket, final TypeNameMatch next) { syncExec(ticket, new Runnable() { public void run() { if (fNextElement > 0) { TableItem item= fTable.getItem(fNextElement - 1); String label= item.getText(); String newLabel= fLabelProvider.getText(null, (TypeNameMatch)item.getData(), next); if (newLabel.length() != label.length()) item.setText(newLabel); if (fLastSelection != null && fLastSelection.length > 0) { TableItem last= fLastSelection[fLastSelection.length - 1]; if (last == item) { fLastLabels[fLastLabels.length - 1]= newLabel; } } } fDashLineIndex= fNextElement; addDashLine(); } }); } private void addDashLine() { TableItem item= null; if (fItems.size() > fNextElement) { item= (TableItem)fItems.get(fNextElement); } else { item= new TableItem(fTable, SWT.NONE); fItems.add(item); } fillDashLine(item); fNextElement++; } private void addSingleElement(Object element, ImageDescriptor imageDescriptor, String label) { TableItem item= null; Object old= null; if (fItems.size() > fNextElement) { item= (TableItem)fItems.get(fNextElement); old= item.getData(); item.setForeground(null); } else { item= new TableItem(fTable, SWT.NONE); fItems.add(item); } item.setData(element); item.setImage(fImageManager.get(imageDescriptor)); if (fNextElement == 0) { if (needsSelectionChange(old, element) || fLastSelection != null) { item.setText(label); fTable.setSelection(0); fTable.notifyListeners(SWT.Selection, new Event()); } else { fLastSelection= new TableItem[] { item }; fLastLabels= new String[] { label }; } } else { item.setText(label); } fNextElement++; } private boolean needsSelectionChange(Object oldElement, Object newElement) { int[] selected= fTable.getSelectionIndices(); if (selected.length != 1) return true; if (selected[0] != 0) return true; if (oldElement == null) return true; return !oldElement.equals(newElement); } private void scheduleSearchJob(int mode) { fSearchJobTicket++; if (fLastCompletedFilter != null && fTypeInfoFilter.isSubFilter(fLastCompletedFilter.getText())) { fSearchJob= new CachedResultJob(fSearchJobTicket, fLastCompletedResult, this, fTypeInfoFilter, fHistory, fNumberOfVisibleItems, mode); } else { fLastCompletedFilter= null; fLastCompletedResult= null; fSearchJob= new SearchEngineJob(fSearchJobTicket, this, fTypeInfoFilter, fHistory, fNumberOfVisibleItems, mode, fSearchScope, fElementKind); } fSearchJob.schedule(); } private void searchJobDone(int ticket) { syncExec(ticket, new Runnable() { public void run() { shortenTable(); checkEmptyList(); fSearchJob= null; } }); } private void searchJobCanceled(int ticket, final boolean removePendingItems) { syncExec(ticket, new Runnable() { public void run() { if (removePendingItems) { shortenTable(); checkEmptyList(); } fSearchJob= null; } }); } private synchronized void searchJobFailed(int ticket, CoreException e) { searchJobDone(ticket); JavaPlugin.log(e); } //-- virtual table support ------------------------------------------------------- private void setHistoryResult(int ticket, final TypeNameMatch[] types) { syncExec(ticket, new Runnable() { public void run() { fExpectedItemCount= types.length; int lastHistoryLength= fHistoryMatches.length; fHistoryMatches= types; int length= fHistoryMatches.length + fSearchMatches.length; int dash= (fHistoryMatches.length > 0 && fSearchMatches.length > 0) ? 1 : 0; fTable.setItemCount(length + dash); if (length == 0) { // bug under windows. fTable.redraw(); return; } int update= Math.max(lastHistoryLength, fHistoryMatches.length); if (update > 0) { fTable.clear(0, update + dash - 1); } } }); } private void setSearchResult(int ticket, final TypeNameMatch[] types) { syncExec(ticket, new Runnable() { public void run() { fExpectedItemCount+= types.length; fSearchMatches= types; int length= fHistoryMatches.length + fSearchMatches.length; int dash= (fHistoryMatches.length > 0 && fSearchMatches.length > 0) ? 1 : 0; fTable.setItemCount(length + dash); if (length == 0) { // bug under windows. fTable.redraw(); return; } if (fHistoryMatches.length == 0) { fTable.clear(0, length + dash - 1); } else { fTable.clear(fHistoryMatches.length - 1, length + dash - 1); } } }); } private void setData(TableItem item) { int index= fTable.indexOf(item); TypeNameMatch type= getTypeInfo(index); if (type == DASH_LINE) { item.setData(fDashLine); fillDashLine(item); } else { item.setData(type); item.setImage(fImageManager.get(fLabelProvider.getImageDescriptor(type))); item.setText(fLabelProvider.getText( getTypeInfo(index - 1), type, getTypeInfo(index + 1))); item.setForeground(null); } } private TypeNameMatch getTypeInfo(int index) { if (index < 0) return null; if (index < fHistoryMatches.length) { return fHistoryMatches[index]; } int dash= (fHistoryMatches.length > 0 && fSearchMatches.length > 0) ? 1 : 0; if (index == fHistoryMatches.length && dash == 1) { return DASH_LINE; } index= index - fHistoryMatches.length - dash; if (index >= fSearchMatches.length) return null; return fSearchMatches[index]; } //-- Sync Job updates ------------------------------------------------------------ private void scheduleSyncJob() { fSyncJob= new SyncJob(this); fSyncJob.schedule(); } private void syncJobDone() { syncExec(new Runnable() { public void run() { fSyncJob= null; if (fTypeInfoFilter != null) { scheduleSearchJob(FULL); } } }); } private boolean isSyncJobRunning() { return fSyncJob != null; } //-- progress monitor updates ----------------------------------------------------- private void scheduleProgressUpdateJob() { syncExec(new Runnable() { public void run() { if (fProgressCounter == 0) { clearProgressMessage(); fProgressUpdateJob= new ProgressUpdateJob(fDisplay, TypeInfoViewer.this); fProgressUpdateJob.schedule(300); } fProgressCounter++; } }); } private void stopProgressUpdateJob() { syncExec(new Runnable() { public void run() { fProgressCounter--; if (fProgressCounter == 0 && fProgressUpdateJob != null) { fProgressUpdateJob.stop(); fProgressUpdateJob= null; clearProgressMessage(); } } }); } private void setProgressMessage(String message) { fProgressMessage= message; } private void clearProgressMessage() { fProgressMessage= ""; //$NON-NLS-1$ fProgressLabel.setText(fProgressMessage); } private void updateProgressMessage() { fProgressLabel.setText(fProgressMessage); } //-- Helper methods -------------------------------------------------------------- private void syncExec(final Runnable runnable) { if (fDisplay.isDisposed()) return; fDisplay.syncExec(new Runnable() { public void run() { if (fTable.isDisposed()) return; runnable.run(); } }); } private void syncExec(final int ticket, final Runnable runnable) { if (fDisplay.isDisposed()) return; fDisplay.syncExec(new Runnable() { public void run() { if (fTable.isDisposed() || ticket != fSearchJobTicket) return; runnable.run(); } }); } private void fillDashLine(TableItem item) { Rectangle bounds= item.getImageBounds(0); Rectangle area= fTable.getBounds(); boolean willHaveScrollBar= fExpectedItemCount + 1 > fNumberOfVisibleItems; item.setText(fDashLine.getText(area.width - bounds.x - bounds.width - fTableWidthDelta - (willHaveScrollBar ? fScrollbarWidth : 0))); item.setImage(fSeparatorIcon); item.setForeground(fDashLineColor); item.setData(fDashLine); } private void shortenTable() { if (VIRTUAL) return; if (fNextElement < fItems.size()) { fTable.setRedraw(false); fTable.remove(fNextElement, fItems.size() - 1); fTable.setRedraw(true); } for (int i= fItems.size() - 1; i >= fNextElement; i--) { fItems.remove(i); } } private void checkEmptyList() { if (fTable.getItemCount() == 0) { fTable.notifyListeners(SWT.Selection, new Event()); } } private void setTableSelection(int index) { fTable.setSelection(index); fTable.notifyListeners(SWT.Selection, new Event()); } private Color computeDashLineColor() { Color fg= fTable.getForeground(); int fGray= (int)(0.3*fg.getRed() + 0.59*fg.getGreen() + 0.11*fg.getBlue()); Color bg= fTable.getBackground(); int bGray= (int)(0.3*bg.getRed() + 0.59*bg.getGreen() + 0.11*bg.getBlue()); int gray= (int)((fGray + bGray) * 0.66); return new Color(fDisplay, gray, gray, gray); } private int computeScrollBarWidth() { Composite t= new Composite(fTable.getShell(), SWT.V_SCROLL); int result= t.computeTrim(0, 0, 0, 0).width; t.dispose(); return result; } private String getQualifiedText(TypeNameMatch type) { return fFullyQualifySelection ? fLabelProvider.getFullyQualifiedText(type) : fLabelProvider.getQualifiedText(type); } }